/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// true_t, false_t
#include "type/typetools.h"
// nx_types
#include "type/typelist.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    clear type qualifiers
*/

template <typename T>
struct NoPoint                          { typedef T ret; };
template <typename T>
struct NoPoint<T*>                      { typedef T ret; };
template <typename T, typename C>
struct NoPoint<T C::*>                  { typedef T ret; };

#define nx_no_point(...) \
    nx::NoPoint<__VA_ARGS__>::ret

template <typename T>
struct NoReference                      { typedef T ret; };
template <typename T>
struct NoReference<T&>                  { typedef T ret; };

#define nx_no_reference(...) \
    nx::NoReference<__VA_ARGS__>::ret

template <typename T>
struct NoArray                          { typedef T ret; };
template <typename T>
struct NoArray<T[]>                     { typedef T ret; };
template <typename T, size_t N>
struct NoArray<T[N]>                    { typedef T ret; };

#define nx_no_array(...) \
    nx::NoArray<__VA_ARGS__>::ret

template <typename T>
struct NoConst                          { typedef T ret; };
template <typename T>
struct NoConst<const T>                 { typedef T ret; };
template <typename T>
struct NoConst<const T*>                { typedef T* ret; };
template <typename T, typename C>
struct NoConst<const T C::*>            { typedef T C::* ret; };
template <typename T, size_t N>
struct NoConst<const T[N]>              { typedef T ret[N]; };
template <typename T>
struct NoConst<const T&>                { typedef T& ret; };

#define nx_no_const(...) \
    nx::NoConst<__VA_ARGS__>::ret

template <typename T>
struct NoVolatile                       { typedef T ret; };
template <typename T>
struct NoVolatile<volatile T>           { typedef T ret; };
template <typename T>
struct NoVolatile<volatile T*>          { typedef T* ret; };
template <typename T, typename C>
struct NoVolatile<volatile T C::*>      { typedef T C::* ret; };
template <typename T, size_t N>
struct NoVolatile<volatile T[N]>        { typedef T ret[N]; };
template <typename T>
struct NoVolatile<volatile T&>          { typedef T& ret; };

#define nx_no_volatile(...) \
    nx::NoVolatile<__VA_ARGS__>::ret

template <typename T>
struct NoQualifier
{
    typedef typename nx_no_const(typename nx_no_volatile(T)) ret;
};

#define nx_no_qualifier(...) \
    nx::NoQualifier<__VA_ARGS__>::ret

/*
    copy type qualifiers
*/

template <typename T, typename R>
struct CopyPoint                        { typedef R ret; };
template <typename T, typename R>
struct CopyPoint<T*, R>                 { typedef R* ret; };
template <typename T, typename C, typename R>
struct CopyPoint<T C::*, R>             { typedef R* ret; };

#define nx_cp_point(...) \
    nx::CopyPoint<__VA_ARGS__>::ret

template <typename T, typename R>
struct CopyReference                    { typedef R ret; };
template <typename T, typename R>
struct CopyReference<T&, R>             { typedef R& ret; };

#define nx_cp_reference(...) \
    nx::CopyReference<__VA_ARGS__>::ret

template <typename T, typename R>
struct CopyArray                        { typedef R ret; };
template <typename T, typename R>
struct CopyArray<T[], R>                { typedef R ret[]; };
template <typename T, typename R, size_t N>
struct CopyArray<T[N], R>               { typedef R ret[N]; };

#define nx_cp_array(...) \
    nx::CopyArray<__VA_ARGS__>::ret

template <typename T, typename R>
struct CopyConst                        { typedef R ret; };
template <typename T, typename R>
struct CopyConst<const T, R>            { typedef const R ret; };
template <typename T, typename R>
struct CopyConst<const T*, R>           { typedef const R ret; };
template <typename T, typename C, typename R>
struct CopyConst<const T C::*, R>       { typedef const R ret; };
template <typename T, size_t N, typename R>
struct CopyConst<const T[N], R>         { typedef const R ret; };
template <typename T, typename R>
struct CopyConst<const T&, R>           { typedef const R ret; };

#define nx_cp_const(...) \
    nx::CopyConst<__VA_ARGS__>::ret

template <typename T, typename R>
struct CopyVolatile                     { typedef R ret; };
template <typename T, typename R>
struct CopyVolatile<volatile T, R>      { typedef volatile R ret; };
template <typename T, typename R>
struct CopyVolatile<volatile T*, R>     { typedef volatile R ret; };
template <typename T, typename C, typename R>
struct CopyVolatile<volatile T C::*, R> { typedef volatile R ret; };
template <typename T, size_t N, typename R>
struct CopyVolatile<volatile T[N], R>   { typedef volatile R ret; };
template <typename T, typename R>
struct CopyVolatile<volatile T&, R>     { typedef volatile R ret; };

#define nx_cp_volatile(...) \
    nx::CopyVolatile<__VA_ARGS__>::ret

template <typename T, typename R>
struct CopyQualifier
{
    typedef typename nx_cp_const(T, typename nx_cp_volatile(T, R)) ret;
};

#define nx_cp_qualifier(...) \
    nx::CopyQualifier<__VA_ARGS__>::ret

/*
    detect point
*/

template <typename T>
struct IsPoint
{
    template <typename U>
    struct detail_
        : type::Judge<false>
    {};

    template <typename U>
    struct detail_<U*>
        : type::Judge<true>
    {};

    template <typename U, typename C>
    struct detail_<U C::*>
        : type::Judge<true>
    {};

    NX_VALUE(bool, nx_bool(detail_<typename nx_no_qualifier(T)>));
};

#define nx_is_point(...) \
    nx_bool(nx::IsPoint<__VA_ARGS__>)

/*
    detect reference
*/

template <typename T>
struct IsReference
{
    template <typename U>
    struct detail_
        : type::Judge<false>
    {};

    template <typename U>
    struct detail_<U&>
        : type::Judge<true>
    {};

    NX_VALUE(bool, nx_bool(detail_<typename nx_no_qualifier(T)>));
};

#define nx_is_reference(...) \
    nx_bool(nx::IsReference<__VA_ARGS__>)

/*
    detect array
*/

template <typename T>
struct IsArray
    : type::Judge<false>
{};

template <typename T>
struct IsArray<T[]>
    : type::Judge<true>
{};

template <typename T, size_t N>
struct IsArray<T[N]>
    : type::Judge<true>
{};

#define nx_is_array(...) \
    nx_bool(nx::IsArray<__VA_ARGS__>)

/*
    detect enum
*/

#define nx_is_enum(...) __is_enum(__VA_ARGS__)

/*
    detect union
*/

#define nx_is_union(...) __is_union(__VA_ARGS__)

/*
    detect class
*/

#define nx_is_class(...) __is_class(__VA_ARGS__)

/*
    detect void
*/

template <typename T>
struct IsVoid
{
    template <typename U, typename Foo_ = void>
    struct detail_
        : type::Judge<false>
    {};

    template <typename Foo_>
    struct detail_<void, Foo_>
        : type::Judge<true>
    {};

    NX_VALUE(bool, nx_bool(detail_<typename nx_no_qualifier(T)>));
};

#define nx_is_void(...) \
    nx_bool(nx::IsVoid<__VA_ARGS__>)

/*
    detect integral
*/

typedef nx_types(bool,  char,  uchar,  wchar,
                 short, ushort, int,   uint, 
                 long,  ulong,  llong, ullong) integral_types_t;

template <typename T>
struct IsIntegral
    : type::Judge<nx_types_exist(integral_types_t, typename nx_no_qualifier(T))>
{};

#define nx_is_integral(...) \
    nx_bool(nx::IsIntegral<__VA_ARGS__>)

/*
    detect floating point
*/

typedef nx_types(float, double, ldouble) float_types_t;

template <typename T>
struct IsFloat
    : type::Judge<nx_types_exist(float_types_t, typename nx_no_qualifier(T))>
{};

#define nx_is_float(...) \
    nx_bool(nx::IsFloat<__VA_ARGS__>)

/*
    detect numeric
*/

template <typename T>
struct IsNumeric
    : type::Judge<nx_is_integral(T) || 
                  nx_is_float   (T)>
{};

#define nx_is_numeric(...) \
    nx_bool(nx::IsNumeric<__VA_ARGS__>)

/*
    detect signed
*/

typedef nx_types(char, short, int, long, llong) signed_types_t;

template <typename T>
struct IsSigned
    : type::Judge<nx_types_exist(signed_types_t, typename nx_no_qualifier(T))>
{};

#define nx_is_signed(...) \
    nx_bool(nx::IsSigned<__VA_ARGS__>)

/*
    detect unsigned
*/

typedef nx_types(uchar, ushort, uint, ulong, ullong) unsigned_types_t;

template <typename T>
struct IsUnsigned
    : type::Judge<nx_types_exist(unsigned_types_t, typename nx_no_qualifier(T))>
{};

#define nx_is_unsigned(...) \
    nx_bool(nx::IsUnsigned<__VA_ARGS__>)

/*
    detect fundamental type
*/

template <typename T>
struct IsFundamental
    : type::Judge<nx_is_numeric(T) ||
                  nx_is_void   (T) ||
                  nx_is_enum   (T) ||
                  nx_is_point  (T)>
{};

#define nx_is_fundamental(...) \
    nx_bool(nx::IsFundamental<__VA_ARGS__>)

/*
    detect POD
*/

#ifdef NX_CC_MSVC

template <typename T>
struct IsPOD
{
    template <typename U>
    struct detail_
        : type::Judge<nx_is_fundamental(U) ||
                      __has_trivial_constructor(U) && __is_pod(U)>
    {};

    NX_VALUE(bool, nx_bool(detail_<typename nx_no_array(T)>));
};

template <>
struct IsPOD<type::null_t>
{
    NX_VALUE(bool, false);
};

#define nx_is_pod(...) nx_bool(nx::IsPOD<__VA_ARGS__>)

#elif defined(NX_CC_GNUC)

#define nx_is_pod(...) __is_pod(__VA_ARGS__)

#endif

/*
    detect container
*/

namespace private_
{
    /* Check for T::iterator */
    template <class T>
    struct check_iterator_
    {
        template<class U>
        static type::yes_t check(typename U::iterator*);
        template<class U>
        static type::not_t check(...);

        NX_VALUE(bool, nx_is_yes(check<T>(NX_NULL)));
    };
}

template <typename T>
struct IsContainer
{
    template <typename U>
    struct detail_
        : type::Judge<nx_bool(private_::check_iterator_<U>) || 
                      nx_is_array(U)>
    {};

    NX_VALUE(bool, nx_bool(detail_<typename nx_no_reference(
                                   typename nx_no_qualifier(T))>));
};

template <>
struct IsContainer<type::null_t>
    : type::Judge<false>
{};

#define nx_is_container(...) nx_bool(nx::IsContainer<__VA_ARGS__>)

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
